﻿namespace com.crownpack.Extensions.DateTimeExtensions
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Xml;

    public static class DateTimeExtensions
    {
        #region Methods

        /// <summary>
        /// Compares this instance with another instance of nullable System.DateTime.
        /// </summary>
        /// <param path="a">Instance of type nullable System.DateTime to use in the comparison.</param>
        /// <param path="a">A comparand of type nullable System.DateTime.</param>
        /// <returns>A 32-bit signed integer indicating the lexical relationship between the two comparands. Value Condition Less than zero This instance is less than a -or- this instance is null. Zero This instance is equal to a -or- this instance and a are both null. Greater than zero This instance is greater than a -or- a is null.</returns>
        public static int CompareTo( this DateTime? a, DateTime? b )
        {
            if ( a == null && b == null )
                return 0;
            if ( a == null )
                return -1;
            if ( b == null )
                return 1;
            if ( a == b )
                return 0;
            if ( a < b )
                return -1;
            if ( a > b )
                return 1;
            return 0;
        }

        public static bool EqualsDate( this DateTime a, DateTime value )
        {
            return ( a.Year == value.Year && a.Month == value.Month && a.Day == value.Day );
        }

        public static DateTime GetAbsoluteEndOfYear( this DateTime date )
        {
            DateTime a = new DateTime( date.Year + 1, 1, 1, 0, 0, 0, 0 );
            return a.SubtractMilliseconds( 1 );
        }

        public static int GetDaysInQuarter( this DateTime a )
        {
            int quarter = a.GetQuarter( );
            return a.GetDaysInQuarter( quarter );
        }

        public static int GetDaysInQuarter( this DateTime a, int quarter )
        {
            DateTime sd;
            DateTime ed;
            int y = a.Year;
            int q = quarter > 0 && quarter <= 4 ? quarter : quarter < 0 ? 1 : 4;
            if ( q < 4 )
            {
                sd = new DateTime( y, ( 3 * q - 2 ), 1 );
                ed = new DateTime( y, ( 3 * q - 2 ) + 3, 1 );
            }
            else
            {
                sd = new DateTime( y, ( 3 * q - 2 ), 1 );
                ed = new DateTime( y + 1, 1, 1 );
            }
            TimeSpan ts = new TimeSpan( ed.Subtract( sd ).Ticks );
            return ts.Days;
        }

        /// <summary>
        /// Returns an instance of System.DateTime that represents the end of the specified day, i.e. 23 hours, 59 minutes, 59 seconds, 999 milliseconds.
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        public static DateTime GetEndOfDay( this DateTime a )
        {
            return new DateTime( a.Year, a.Month, a.Day, 23, 59, 59, 999 );
        }

        public static DateTime GetFirstDayOfCalendar( this DateTime date )
        {
            DateTime d = new DateTime( date.Year, date.Month, 1 );
            int i = ( int ) d.DayOfWeek;
            int p = 7 - ( 7 - i );
            if ( p > 0 )
            {
                d = d.Subtract( new TimeSpan( p, 0, 0, 0 ) );
            }
            return d;
        }

        public static DateTime GetLastDayOfCalendar( this DateTime date )
        {
            DateTime a = date.AddMonths( 1 );
            DateTime d = new DateTime( a.Year, a.Month, 1 );
            int i = ( int ) d.DayOfWeek;
            int p = 7 - ( i + 1 );
            if ( p > 0 )
            {
                d = d.Add( new TimeSpan( p, 0, 0, 0 ) );
            }
            return d;
        }

        public static int GetQuarter( this DateTime a )
        {
            DateTime d = a;
            int m = d.Month;
            return m % 3 == 0 ? m / 3 : m / 3 + 1;
        }

        public static DateTime GetQuarterEndDate( this DateTime a )
        {
            int quarter = a.GetQuarter( );
            return a.GetQuarterEndDate( quarter );
        }

        public static DateTime GetQuarterEndDate( this DateTime a, int quarter )
        {
            int y = a.Year;
            int q = quarter > 0 && quarter <= 4 ? quarter : quarter < 0 ? 1 : 4;
            int m = ( q * 3 ) + 1;
            if ( m > 12 )
            {
                m = 1;
                y++;
            }
            return new DateTime( y, m, 1 ).AddDays( -1 );
        }

        public static List<string> GetQuarterMonthNames( this DateTime a )
        {
            int quarter = a.GetQuarter( );
            List<string> l = new List<string>( );
            for ( int i = quarter * 3 - 2; i <= quarter * 3; i++ )
                l.Add( new DateTime( a.Year, i, 1 ).ToString( "MMMM" ) );
            return l;
        }

        public static DateTime GetQuarterStartDate( this DateTime a )
        {
            int quarter = a.GetQuarter( );
            return a.GetQuarterStartDate( quarter );
        }

        public static DateTime GetQuarterStartDate( this DateTime a, int quarter )
        {
            int y = a.Year;
            int q = quarter > 0 && quarter <= 4 ? quarter : quarter < 0 ? 1 : 4;
            return new DateTime( y, q * 3 - 2, 1 );
        }

        /// <summary>
        /// Returns an instance of System.DateTime that represents the start of the specified day, i.e. 0 hour, 0 minute, 0 second, 0 millisecond.
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        public static DateTime GetStartOfDay( this DateTime a )
        {
            return new DateTime( a.Year, a.Month, a.Day, 0, 0, 0, 0 );
        }

        /// <summary>
        /// Returns the Coordinated Universal Time (UTC) offset for the specified local time.
        /// </summary>
        /// <param name="a"></param>
        /// <returns>Coordinated Universal Time (UTC) offset as a TimeSpan.</returns>
        public static TimeSpan GetUtcOffset( this DateTime a )
        {
            return TimeZone.CurrentTimeZone.GetUtcOffset( a );
        }

        /// <summary>
        /// Returns the Coordinated Universal Time (UTC) offset for the specified local time adjusted for daylight saving time periods.
        /// </summary>
        /// <param name="a"></param>
        /// <returns>Coordinated Universal Time (UTC) offset as a TimeSpan.</returns>
        public static TimeSpan GetUtcOffsetAdjusted( this DateTime a )
        {
            TimeSpan t = TimeZone.CurrentTimeZone.GetUtcOffset( a );
            if ( TimeZone.CurrentTimeZone.IsDaylightSavingTime( a ) )
                t = t.Subtract( new TimeSpan( 1, 0, 0 ) );
            return t;
        }

        /// <summary>
        /// Returns the week of the year that includes the date in the specified System.DateTime.
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        public static int GetWeekOfYear( this DateTime a )
        {
            DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
            Calendar calendar = dfi.Calendar;
            return calendar.GetWeekOfYear( a, dfi.CalendarWeekRule, dfi.FirstDayOfWeek );
        }

        /// <summary>
        /// Returns a value indicating whether the specified date and time is within a daylight saving time period.
        /// </summary>
        /// <param name="a"></param>
        /// <returns>true if the specified date and time is within a daylight saving time period; otherwise false.</returns>
        public static bool IsDaylightSavingTime( this DateTime a )
        {
            return TimeZone.CurrentTimeZone.IsDaylightSavingTime( a );
        }

        public static bool IsWeekend( this DateTime a )
        {
            return ( a.DayOfWeek == DayOfWeek.Saturday || a.DayOfWeek == DayOfWeek.Sunday ) ? true : false;
        }

        public static DateTime SubtractDays( this DateTime a, double value )
        {
            if ( a == null )
                return new DateTime( );
            return a.AddDays( 0 - value );
        }

        public static DateTime SubtractHours( this DateTime a, double value )
        {
            if ( a == null )
                return new DateTime( );
            return a.AddHours( 0 - value );
        }

        public static DateTime SubtractMilliseconds( this DateTime a, double value )
        {
            if ( a == null )
                return new DateTime( );
            return a.AddMilliseconds( 0 - value );
        }

        public static DateTime SubtractMinutes( this DateTime a, double value )
        {
            if ( a == null )
                return new DateTime( );
            return a.AddMinutes( 0 - value );
        }

        public static DateTime SubtractMonths( this DateTime a, int months )
        {
            if ( a == null )
                return new DateTime( );
            return a.AddMonths( 0 - months );
        }

        public static DateTime SubtractSeconds( this DateTime a, double value )
        {
            if ( a == null )
                return new DateTime( );
            return a.AddSeconds( 0 - value );
        }

        public static DateTime SubtractTicks( this DateTime a, long value )
        {
            if ( a == null )
                return new DateTime( );
            return a.AddTicks( 0 - value );
        }

        public static DateTime SubtractYears( this DateTime a, int value )
        {
            if ( a == null )
                return new DateTime( );
            return a.AddYears( 0 - value );
        }

        public static string ToLongDateTimeString( this DateTime a )
        {
            return string.Format( "{0} {1}", a.ToLongDateString( ), a.ToLongTimeString( ) );
        }

        /// <summary>
        /// Convert the DateTime Year to the equivalent Roman Numeral.
        /// </summary>
        /// <param name="a">Instance of type System.DateTime of which the Year is converted.</param>
        /// <returns>Returns an instance of RomanNumeral.</returns>
        public static RomanNumeral ToRomanNumeral( this DateTime a )
        {
            return new RomanNumeral( a );
        }

        /// <summary>
        /// Converts the System.DateTime to its equivalent short date and time string representation.
        /// </summary>
        /// <param name="a">Instance of type System.DateTime to be converted.</param>
        /// <returns>String representation of the specified date and time.</returns>
        public static string ToShortDateTimeString( this DateTime a )
        {
            return string.Format( "{0} {1}", a.ToShortDateString( ), a.ToShortTimeString( ) );
        }

        /// <summary>
        /// Converts the System.DateTime to a System.String treating the date and time value as a local time. Returns as 2012-09-21T16:45:13Z.
        /// </summary>
        /// <param path="a">Instance of type System.DateTime to be converted.</param>
        /// <returns>String representation of the specified date and time.</returns>
        public static string ToShortXml( this DateTime a )
        {
            if ( a == null )
                return string.Empty;
            return XmlConvert.ToString( a, "yyyy-MM-ddTHH:mm:ssZ" );
        }

        /// <summary>
        /// Converts the System.DateTime to a System.String treating the date and time value as a local time and converted to a Utc.
        /// </summary>
        /// <param path="value">Instance of type System.DateTime to be converted.</param>
        /// <returns>String representation of the specified date and time as Utc.</returns>
        public static string ToUtcXml( this DateTime a )
        {
            if ( a == null )
                return string.Empty;
            return XmlConvert.ToString( a, XmlDateTimeSerializationMode.Utc );
        }

        /// <summary>"2012-09-24T17:01:29.3554687Z"
        /// Converts the System.DateTime to a System.String treating the date and time value as a local time. Returns as 2012-09-21T16:45:13.3554687Z
        /// </summary>
        /// <param path="a">Instance of type System.DateTime to be converted.</param>
        /// <returns>String representation of the specified date and time.</returns>
        public static string ToXml( this DateTime a )
        {
            if ( a == null )
                return string.Empty;
            return XmlConvert.ToString( a, "yyyy-MM-ddTHH:mm:ssZzzzzz" );
        }

        #endregion Methods
    }
}